﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<deque>
using namespace std;

//继承:是⾯向对象程序设计使代码可以复⽤的最重要的⼿段
//class Person  //父类(基类)
//{
//public:
//	// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
//	void identity()
//	{
//		cout << "void identity()" << _name << endl;
//	}
//protected:
//	string _name = "张三"; // 姓名
//	string _address; // 地址
//	string _tel; // 电话
//	int _age = 18; // 年龄
//};
//class Student : public Person//学生这个子类(派生类)继承了Person这个父类
//{
//public:
//	// 学习
//	void study()
//	{
//		// ...
//	}
//		protected:
//		int _stuid; // 学号
//};
//class Teacher : public Person//老师这个子类(派生类)继承了Person这个父类
//{
//public:
//	// 授课
//	void teaching()
//	{
//		//...
//	}
//protected:
//	string title; // 职称
//};
//int main()
//{
//	Student s;
//	Teacher t;
//	s.identity();
//	t.identity();
//	return 0;
//}


//继承类模板
//namespace fpj
//{
//	template<class T>
//	class stack :public std::vector<int>//继承类模板的方式  class xxx:public std::xxx(需要指定leiyu)
//	{
//	public:
//		void push(const T& x)
//		{
//			//push_back(x);//error
//			vector<T>::push_back(x);//⽗类是类模板时，需要指定⼀下类域，
//		}
//		void pop()
//		{
//			vector<T>::pop_back();//⽗类是类模板时，需要指定⼀下类域
//		}
//		const T& top()
//		{
//			return vector<T>::back();//⽗类是类模板时，需要指定⼀下类域
//		}
//		bool empty()
//		{
//			return vector<T>::empty();//⽗类是类模板时，需要指定⼀下类域
//		}
//	};
//}
//int main()
//{
//	fpj::stack<int> st;
//	st.push(1);
//	st.push(2);
//	st.push(3);
//	while (!st.empty())
//	{
//		cout << st.top() << " ";
//		st.pop();
//	}
//	return 0;
//}


//public继承的⼦类对象 可以赋值给⽗类的对象/⽗类的指针/⽗类的引⽤,这⾥有个形象的说法叫切⽚或者切割。寓意把⼦类中⽗类那部分切来赋值过去
//class Person
//{
//protected:
//	string _name; // 姓名
//	string _sex; // 性别
//	int _age; // 年龄
//};
//class Student : public Person
//{
//public:
//	int _No; // 学号
//};
//int main()
//{
//	Student sobj;
//	// 1.⼦类对象可以赋值给⽗类对象/指针/引⽤
//	Person pobj = sobj;//赋值给父类对象
//	Person* pp = &sobj;//赋值给父类指针
//	Person& rp = sobj;//赋值给父类引用
//
//	//2.⽗类对象不能赋值给⼦类对象，这⾥会编译报错
//	//sobj = pobj;
//	return 0;
//}


//在继承中父类和子类都有独立的作用域

//⼦类和⽗类中有同名成员，⼦类成员将屏蔽⽗类对同名成员的直接访问，这种情况叫隐藏。
//class Person
//{
//protected:
//	string _name = "⼩李⼦"; // 姓名
//	int _num = 111; // ⾝份证号
//};
//class Student : public Person
//{
//public:
//	void Print()
//	{
//		cout << " 姓名:" << _name << endl;
//		//cout << " ⾝份证号:" << Person::_num << endl;//（在⼦	类成员函数中，可以使⽤ ⽗类::⽗类成员 显⽰访问）去掉注释输出为111
//		cout << " 学号:" << _num << endl;
//	}
//protected:
//	int _num = 999; // 学号
//};
//int main()
//{
//	Student s1;
//	s1.Print();//⼦类和⽗类中有同名成员，⼦类成员将屏蔽⽗类对同名成员的直接访问;输出为999
//	return 0;
//};


//需要注意的是如果是成员函数的隐藏，只需要函数名相同就构成隐藏。
//A和B类中的两个func构成隐藏关系
//class A
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//};
//class B : public A
//{
//public:
//	void fun(int i)
//	{
//		cout << "func(int i)" << i << endl;
//	}
//};
//int main()
//{
//	B b;
//	b.fun(10);
//	//b.fun();不能直接访问A中的成员函数
//	b.A::fun();//这样才是ok的
//	return 0;
//};


//子类的默认成员函数
class Person//父类
{
public:
	//Person(const char* name = "peter")//构造函数
	Person(const char* name)//构造函数  如果⽗类没有默认的构造函数，则必须在⼦类构造函数的初始化列表阶段显⽰调⽤。
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	
	Person(const Person& p)//拷贝
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person & operator=(const Person & p)//赋值
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()//析构
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; 
};

class student :public Person//子类
{
public:
	// 默认生成的构造函数的行为:
	// 1、内置类型->不确定
	// 2、自定义类型->调用默认构造
	// 3、继承父类成员看做一个整体对象，要求调用父类的默认构造

	// 严格说Student拷贝构造默认生成的就够用了
	// 如果有需要深拷贝的资源，才需要自己实现
	student(const student& s)//自己是实现的
		:Person(s)//调用父类的引用
		, _num(s._num)
		, _addrss(s._addrss)
	{
		// 深拷贝
	}

	// 严格说Student赋值重载默认生成的就够用了
	// 如果有需要深拷贝的资源，才需要自己实现
	student& operator=(const student& s)
	{
		if (this != &s)
		{
			Person::operator=(s);// 父类和子类的operator=构成隐藏关系
			_num = s._num;
			_addrss = s._addrss;
		}
		return *this;
	}

	// 严格说Student析构默认生成的就够用了
	// 如果有需要显示释放的资源，才需要自己实现
	~student()
	{
		//~Person();//子类的析构和父类析构函数也构成隐藏关系;直接调父类是不可以的

		//析构不需要显示调用,子类析构函数之后,会自动调用父类析构
		//Person::~Person();//需要指定作用域
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	student(const char* name, int num, const char* addrss)
		:Person(name)//如果⽗类没有默认的构造函数，则必须在⼦类构造函数的初始化列表阶段显⽰调⽤
		, _num(num)
		,_addrss(addrss)
	{}
protected:
	int _num;
	string _addrss;
};

int main()
{
	//student s;//⼦类的构造函数必须调⽤⽗类的构造函数初始化⽗类的那⼀部分成员。通过调试这里可以看出(有默认构造)
	
	student s("张三", 1, "河南省");

	student s1(s);//拷贝构造:⼦类的拷⻉构造函数必须调⽤⽗类的拷⻉构造完成⽗类的拷⻉初始化。

	student s2("李四", 2, "安阳市");//⼦类的operator=必须要调⽤⽗类的operator=完成⽗类的复制。调试这里即可
	s = s2;//赋值


	return 0;
}
